﻿--[[ QueueAssistant 0.1.0 ]]--

-- Namespaces
QueueAssistant = {}
QueueAssistant.TEXT = {}

-- Aliases
QA = QueueAssistant

-- Options
QA.ENABLED = true
QA.FRAME_LOCKED = false

-- Debug options
QA.DEBUG = true
QA.DEBUG_PREFIX = "QA - "

-- All functions to process events are in this directionary
QA.EVENT_HANDLER = {}

QA.JOIN_BUTTON_NAME = "QAJoinButton"
QA.JOIN_BUTTON_MAX = 5
QA.STATUS_BUTTON_NAME = "QAJoinButtonStatus"
QA.LABEL_BATTLEFIELD_NAME = "QALabelBattlefieldInQueue"

QA.PrevLastBFsSeen = {}
QA.LastBFsSeen = {}
QA.MostlyNewBFs = {}
QA.KnownBFs = {}

QA.BFID2Index = {}
QA.LastSearchList = {}
QA.MostlyNewSearchList = {}
QA.KnownBFsSearchList = {}

QA.PendingToJoin = {}

QA.MostlyNewLifeTime = 40 -- in seconds
QA.LastSeenLifeTime = 90 -- in seconds
QA.LastSeenFromNow = 120 -- in seconds
QA.PendingTimeout = 10 -- in seconds

QA.MAX_BATTLEFIELD_QUEUES = 3
QA.QueueStatus = {};
QA.QueueList = {};

QA.TEXT.NEW = "|CFF00FF00(New)|r"
QA.TEXT.OLD = "|CFFFF0000(Old)|r"
QA.TEXT.PERHAPS = "|CFFFFD100(Perhaps)|r"
QA.TEXT.UNKNOWN = "(Unknown)"
QA.TEXT.LEFT_CLICK_ENTER = "Left Click to " .. ENTER_BATTLE
QA.TEXT.RIGHT_CLICK_LEAVE = "Right Click to " .. LEAVE_QUEUE
QA.TEXT.TOOLTIP_IN_QUEUE = BATTLEFIELD_IN_QUEUE .. "\n\n" .. QA.TEXT.RIGHT_CLICK_LEAVE;
QA.TEXT.TOOLTIP_QUEUE_CONFIRM = BATTLEFIELD_QUEUE_CONFIRM .. "\n\n" .. QA.TEXT.LEFT_CLICK_ENTER .. "\n" .. QA.TEXT.RIGHT_CLICK_LEAVE
QA.TEXT.SHOW_QA = "Show >>"
QA.TEXT.HIDE_QA = "<< Hide"
QA.TEXT.TOOLTIP_SHOW_QA = "Show QueueAssistant Panel";
QA.TEXT.TOOLTIP_HIDE_QA = "Hide QueueAssistant Panel";

function QA_Reset()
	QA.PrevLastBFsSeen = {}
	QA.LastBFsSeen = {}
	QA.MostlyNewBFs = {}
	QA.KnownBFs = {}
	QA.BFID2Index = {}
	QA.LastSearchList = {}
	QA.MostlyNewSearchList = {}
	QA.KnownBFsSearchList = {}
	QA.PendingToJoin = {}
	QA.QueueStatus = {};
	QA.QueueList = {};
end

function QA_Enabled( switch )
	if QA.ENABLED == switch then
		return;
	end
	QA.ENABLED = switch;
	if switch == false then
		HideUIPanel(QueueAssistantFrame);
		HideUIPanel(BattlefieldFrame);
	end
	QA_Reset();
end

function QADebugInfo( debugMsg )
    if( QA.DEBUG ) then
        DEFAULT_CHAT_FRAME:AddMessage( QA.DEBUG_PREFIX .. debugMsg);
    end
end

function QAColoredText( inText, inColor )
    return inText;
end

function QA_CmdHandler( cmd )
	QADebugInfo("cmd: " .. cmd);
	local c;
	
	c = string.match( cmd, "^(on)%s*$" );
	if c == "on" then
		QA_Enabled( true );
		return;
	end
	
	c = string.match( cmd, "^(off)%s*$" );
	if c == "off" then
		QA_Enabled( false );
		return;
	end
end
    
function QueueAssistant_OnEvent( event )
	if QA.ENABLED ~= true then
		return;
	end
    --// Start debug info
    QADebugInfo( "Received event " .. event );
    --// End debug info
    QA.EVENT_HANDLER[event]( arg1, arg2, arg3, arg4, arg5 );
end

function QueueAssistant_OnLoad()
    this:RegisterEvent("PLAYER_ENTERING_WORLD");
	this:RegisterEvent("BATTLEFIELDS_SHOW");
	this:RegisterEvent("BATTLEFIELDS_CLOSED");
	this:RegisterEvent("UPDATE_BATTLEFIELD_STATUS");
    this:RegisterEvent("PARTY_LEADER_CHANGED");
	
	SlashCmdList["QUEUEASSISTANT"] = QA_CmdHandler;
	SLASH_QUEUEASSISTANT1 = "/queueassistant";
	SLASH_QUEUEASSISTANT2 = "/qa";

    HideUIPanel(QueueAssistantFrame);
	HideUIPanel(BattlefieldFrame);
    
    if StaticPopupDialogs["CONFIRM_BATTLEFIELD_ENTRY"] then
        local dlg = StaticPopupDialogs["CONFIRM_BATTLEFIELD_ENTRY"];
        
        dlg.OnCancel = function( qNum, reason)
            if QA.ENABLED ~= true then
				return;
			end
			if IsControlKeyDown() and reason == "clicked" then
                AcceptBattlefieldPort( qNum, nil );
            end
        end
        
        dlg.OnUpdate = function()
			if QA.ENABLED ~= true then
				return;
			end
            getglobal( this:GetName() .. "Button2" ):SetText(HIDE);
            if IsControlKeyDown() then
                getglobal( this:GetName() .. "Button2" ):SetText(LEAVE_QUEUE);
            end
        end
        
        dlg.OnShow = function()
            --QADebugInfo( "---- Now entering dlg.OnShow ----");
			if QA.ENABLED ~= true then
				return;
			end
            
            local currText = getglobal( this:GetName() .. "Text" ):GetText();
            local modifiedText;
            local currTime = time();
            local bfName = "";
            local bfID;
            local i;
            
            for i=1, table.getn(QA.KnownBFs) do
                if string.find( currText, QA.KnownBFs[i] ) then
                    bfName = QA.KnownBFs[i];
                end
            end
            bfID = tonumber( string.match(currText, "%d+") );
			
            if bfName == "" or ( QA.LastSeenFromNow < currTime - QA.LastBFsSeen[bfName].UpdatedTime and QA.QueueStatus[bfName].isNewText == QA.TEXT.UNKNOWN ) then
                modifiedText = string.gsub( currText, "%d+", bfID .. " " .. QA.TEXT.UNKNOWN);
                getglobal(this:GetName() .. "Text"):SetText(modifiedText);
                return;
            end
            --QADebugInfo(" bfName: " .. bfName .. " bfID: " .. bfID );
			if QA.QueueStatus[bfName].isNewText == nil then
				return;
			end
			
            if QA.QueueStatus[bfName].isNewText ~= QA.TEXT.UNKNOWN then
                modifiedText = string.gsub( currText, "%d+", bfID .. " " .. QA.QueueStatus[bfName].isNewText);
                getglobal(this:GetName() .. "Text"):SetText(modifiedText);
                return;
            end
            
            QA_RemoveDeadMostlyNew( bfName );
            if QA.LastSearchList[bfName][bfID ..""] ~= true then
                QA.QueueStatus[bfName].isNewText = QA.TEXT.NEW;
            elseif QA.MostlyNewSearchList[bfName][bfID .. ""] == true then
                QA.QueueStatus[bfName].isNewText = QA.TEXT.PERHAPS;
            else
                QA.QueueStatus[bfName].isNewText = QA.TEXT.OLD;
            end

			modifiedText = string.gsub( currText, "%d+", bfID .. " " .. QA.QueueStatus[bfName].isNewText);
            getglobal( this:GetName() .. "Text" ):SetText(modifiedText);
            
			--QADebugInfo( "isNewText: " .. isNewText);
        end
            
    end
end

QA.EVENT_HANDLER["PLAYER_ENTERING_WORLD"] = function()
end

QA.EVENT_HANDLER["BATTLEFIELDS_SHOW"] = function()
    local bfName = GetBattlefieldInfo();
    QueueAssistantFrame.title = bfName;
    QA_UpdateBFsSeen( bfName );
    QA_JoinPendingBF( bfName );
    QA_UpdateMostlyNewBFs( bfName );
    QA_UpdateQAFrame( bfName );
    
    ShowUIPanel(QueueAssistantFrame);
end

QA.EVENT_HANDLER["BATTLEFIELDS_CLOSED"] = function()
    HideUIPanel(QueueAssistantFrame);
    QueueAssistantFrame.title = nil;
end

QA.EVENT_HANDLER["UPDATE_BATTLEFIELD_STATUS"] = function()
    local status, mapName, instanceID;
    local i;
    --QADebugInfo("#### start battlefield status");
    --// Start of debug info
--[[	if QA.DEBUG == true then
		if QueueAssistantFrame.title then
			QADebugInfo( "QueueAssistantFrame.title = " .. QueueAssistantFrame.title );
		else
			QADebugInfo( "QueueAssistantFrame.title = nil" );
		end
	end]]
	--// End of debug info
    for i=1, QA.MAX_BATTLEFIELD_QUEUES, 1 do
        status, mapName, instanceID = GetBattlefieldStatus(i);
		
		if QA.QueueList[i] == nil then
			QA.QueueList[i] = {};
		end
		QA.QueueList[i].BFName = mapName;
		QA.QueueList[i].Status = status;
		QA.QueueList[i].ID = instanceID;
        
		if mapName then
			if QA.QueueStatus[mapName] == nil then
				QA.QueueStatus[mapName] = {};
			end
            QA.QueueStatus[mapName].Status = status;
            QA.QueueStatus[mapName].QNum = i;
            QA.QueueStatus[mapName].ID = instanceID;
			if status ~= "confirm" then
                QA.QueueStatus[mapName].isNewText = QA.TEXT.UNKNOWN;
            end
            --QADebugInfo( string.format("QA.QueueStatus[%s]-> Status: %s  QNum: %d, ID: %d", mapName, status, i, instanceID) );
			if mapName == QueueAssistantFrame.title then
				QA_UpdateStatusButton(mapName);
			end
		end
	end
    QADebugInfo("#### end battlefield status");   
end

QA.EVENT_HANDLER["PARTY_LEADER_CHANGED"] = function()
end

function QA_UpdateBFsSeen( bfName )
    local numBFs;
    local BFList = {};
    local i;

    numBFs = GetNumBattlefields();
    
    if QA.KnownBFsSearchList[bfName] == nil then
        table.insert( QA.KnownBFs, bfName );
        QA.KnownBFsSearchList[bfName] = true;
    end
    QA.PrevLastBFsSeen[bfName] = nil;
    QA.PrevLastBFsSeen[bfName] = QA.LastBFsSeen[bfName];
    QA.LastBFsSeen[bfName] = BFList;
    BFList.UpdatedTime = time();
	BFList.NumBFs = numBFs;

    --QADebugInfo( "QA_UpdateBFsSeen(): bfName=" .. bfName .. " numBFs=" .. numBFs);
    QA.BFID2Index[bfName] = nil;
    QA.BFID2Index[bfName] = {}
    QA.BFID2Index[bfName]["0"] = 0;
    for i=0, numBFs-1, 1 do
        BFList[i] = GetBattlefieldInstanceInfo( numBFs - i );
        QA.BFID2Index[bfName][BFList[i] .. ""] = numBFs - i;
        --QADebugInfo( "QA_UpdateBFsSeen(): " .. string.format('QA.LastBFsSeen[%s][%d]=%d', bfName, i, QA.LastBFsSeen[bfName][i]) );
    end
    QA_UpdateLastSearchList( bfName );
    
    return bfName;
end

function QA_RemoveDeadMostlyNew( bfName )
    local currTime = time();
    local n, i, interval;
    
    --QADebugInfo("---- Now removing dead mostly new battlefields ----");
    if QA.MostlyNewBFs[bfName][0] ~= nil then
        n = table.getn( QA.MostlyNewBFs[bfName] );
        for i=n, 0, -1 do
            interval = currTime - QA.MostlyNewBFs[bfName][i].UpdatedTime
            if interval > QA.MostlyNewLifeTime then
                --// Start debug info
                if QA.DEBUG == true then
                    --QADebugInfo(string.format('remove - QA.MostlyNewBFs[%s][%d], CurrTime: %d UpdatedTime: %d, interval: %d', bfName, i, QA.MostlyNewBFs[bfName][i].UpdatedTime, currTime, interval));
                    local di
                    for di=0, table.getn(QA.MostlyNewBFs[bfName][i]), 1 do
                        --QADebugInfo(string.format('       - QA.MostlyNewBFs[%s][%d][%d] = %d', bfName, i, di, QA.MostlyNewBFs[bfName][i][di]));
                    end
                end
                --// End debug info
                if i==0 then
                    QA.MostlyNewBFs[bfName] = nil;
                    QA.MostlyNewBFs[bfName] = {};
                else
                    table.remove( QA.MostlyNewBFs[bfName] );
                end
            else
                break;
            end
        end
    end
    QA_UpdateMostlyNewSearchList( bfName );
    --QADebugInfo("---- End of removing dead mostly new battlefields ----");
end

function QA_UpdateMostlyNewBFs( bfName )
    local currTime = time();
    local lastSeen;
    local prevLastSeen;
    local mostlyNewBFs;
    local i, k, n, interval, bfList;
    local inserted = false;
    
    --QADebugInfo( "---- Now entering QA_UpdateMostlyNewBFs() ----");
    
    if QA.MostlyNewBFs[bfName] == nil then
        QA.MostlyNewBFs[bfName] = {};
    end

    lastSeen = QA.LastBFsSeen[bfName];
    prevLastSeen = QA.PrevLastBFsSeen[bfName];
    
    if( not prevLastSeen or not prevLastSeen.UpdatedTime or not prevLastSeen[0] or not lastSeen or not lastSeen.UpdatedTime or not lastSeen[0]) then
        --QA_UpdateMostlyNewSearchList( bfName );
        return;
    end
    
    interval = lastSeen.UpdatedTime - prevLastSeen.UpdatedTime
    --// Start debug info
    --QADebugInfo(" interval between lastSeen and prevLastSeen " .. interval);
    --// End debug info
    if interval > QA.LastSeenLifeTime then
        --QA_UpdateMostlyNewSearchList( bfName );
        return;
    end
    
    bfList = {};
    for i, k in ipairs( prevLastSeen ) do
        bfList[k .. ""] = i;
    end
    if prevLastSeen[0] ~= nil then
        bfList[prevLastSeen[0] .. ""] = 0;
    end
    
    for i=0, table.getn(lastSeen), 1 do
        if bfList[lastSeen[i] ..""] == nil then
            if inserted == false then
                inserted = true;
                table.insert( QA.MostlyNewBFs[bfName], 0, {} );
                QA.MostlyNewBFs[bfName][0].UpdatedTime = currTime;
            end
            table.insert( QA.MostlyNewBFs[bfName][0], 0, lastSeen[i]);
            --QADebugInfo(string.format("insert - lastSeen[%d] = %d", i, lastSeen[i]));
        end
    end
    
    --QA_UpdateMostlyNewSearchList( bfName );
    
    --// Start debug info
    if QA.DEBUG == true then
        --QADebugInfo("---- Start availiable mostly new battlefields ----");
        local dn = table.getn( QA.MostlyNewBFs[bfName] );
        local di, dj;
        for di=0, dn, 1 do
            if QA.MostlyNewBFs[bfName][di] ~= nil then
                --QADebugInfo(string.format('* QA.MostlyNewBFs[%s][%d] - Updated time %d', bfName, di, QA.MostlyNewBFs[bfName][di].UpdatedTime));
                for dj=0, table.getn( QA.MostlyNewBFs[bfName][di] ), 1 do
                    --QADebugInfo(string.format('    QA.MostlyNewBFs[%s][%d][%d] = %d', bfName, di, dj, QA.MostlyNewBFs[bfName][di][dj]));
                end
            end
        end
        --QADebugInfo("---- End availiable mostly new battlefields ----");
    end
    --// End debug info
end

function QA_UpdateLastSearchList( bfName )
    local i, n;
    
    QA.LastSearchList[bfName] = nil;
    QA.LastSearchList[bfName] = {};
    QA.LastSearchList[bfName].UpdatedTime = time();
    
    n = table.getn( QA.LastBFsSeen[bfName] );
    for i=0, n, 1 do
        if QA.LastBFsSeen[bfName][i] ~= nil then
            QA.LastSearchList[bfName][QA.LastBFsSeen[bfName][i] .. ""] = true;
            --QADebugInfo( string.format( "QA.LastSearchList[%s] : %s", bfName, QA.LastBFsSeen[bfName][i] .. "") );
        end
    end
end

function QA_UpdateMostlyNewSearchList( bfName )
    local i, j, n, o;
    --QADebugInfo("-- Update QA.MostlyNewSearchList[" .. bfName .. "]");
    QA.MostlyNewSearchList[bfName] = nil;
    QA.MostlyNewSearchList[bfName] = {};
    QA.MostlyNewSearchList[bfName].UpdatedTime = time();
    
    n = table.getn( QA.MostlyNewBFs[bfName] );
    for i=0, n, 1 do
        if QA.MostlyNewBFs[bfName][i] ~= nil then
            o = table.getn(QA.MostlyNewBFs[bfName][i]);
            for j=0, o, 1 do
                QA.MostlyNewSearchList[bfName][QA.MostlyNewBFs[bfName][i][j] .. ""] = true;
            end
        end
    end
end

function QA_JoinPendingBF( bfName )
    local idx;
    
	if bfName ~= QueueAssistantFrame.title then
        return;
    end
    
	if QA.PendingToJoin[bfName] == nil then
        return;
    end
	
	if QA.PendingTimeout < time() - QA.PendingToJoin[bfName].AddedTime then
		QA.PendingToJoin[bfName] = nil;
		return;
	end
	
    if QA.BFID2Index[bfName] == nil then
		QA.PendingToJoin[bfName] = nil;
        return;
    end
    
    idx = QA.BFID2Index[bfName][QA.PendingToJoin[bfName].ID .. ""];
    QA.PendingToJoin[bfName] = nil;
    JoinBattlefield(idx);
end

function QA_UpdateQAFrame( bfName )
    
    QA_RemoveDeadMostlyNew( bfName );
    QA_UpdateJoinButtons( bfName );
    QA_UpdateStatusButton( bfName );
    
end

function QA_ShowHideButton_OnClick( self )
	if QueueAssistantFrame:IsShown() then
		HideUIPanel( QueueAssistantFrame );
		this:SetText( QA.TEXT.SHOW_QA );
		this.tooltip = QA.TEXT.TOOLTIP_SHOW_QA;
	else
		ShowUIPanel( QueueAssistantFrame );
		this:SetText( QA.TEXT.HIDE_QA );
		this.tooltip = QA.TEXT.TOOLTIP_HIDE_QA;
	end
end

function QA_UpdateJoinButtons( bfName )
	local i, bfID;
	local button;
	local searchList = QA.MostlyNewSearchList[bfName];
	
    for i=1, QA.JOIN_BUTTON_MAX, 1 do
		button = getglobal(QA.JOIN_BUTTON_NAME .. i);
		if QA.LastBFsSeen[bfName][i-1] ~= nil then
			bfID = QA.LastBFsSeen[bfName][i-1];
			button:SetText( bfName .. " " .. bfID);
			if searchList[bfID .. ""] ~= nil then
				button:SetTextFontObject("GameFontHighlight");
                button:SetHighlightFontObject("GameFontHighlight");
			else
				button:SetTextFontObject("GameFontNormal");
                button:SetHighlightFontObject("GameFontNormal");
			end
			button:Show();
		else
			button:Hide();
		end
	end
end

function QA_UpdateStatusButton( bfName )
    local status, qNum, bfID;
    local statusText;
    local found = false; -- To see if battlefield named bfName is in queue and its status is not "none"
    local statusBtn = getglobal(QA.STATUS_BUTTON_NAME);
    local labelBtn = getglobal(QA.LABEL_BATTLEFIELD_NAME);
    local i, idx;
    
    statusBtn:Disable();
    statusBtn:Hide();
    labelBtn:Hide();
    
    for i=1, QA.MAX_BATTLEFIELD_QUEUES, 1 do
        if QA.QueueList[i] ~= nil and bfName == QA.QueueList[i].BFName and QA.QueueList[i].Status ~= "none" then
            found = true;
            status = QA.QueueList[i].Status;
            qNum = i;
            bfID = QA.QueueList[i].ID;
            break;
        end
    end
    
    if not found then
        return;
    end
    
    statusBtn.QNum = qNum;
    
    if status == "confirm" then
        statusBtn:SetText(BATTLEFIELD_CONFIRM_STATUS);
    elseif status == "queued" then
        statusBtn:SetText(BATTLEFIELD_QUEUE_STATUS);
    else
        statusBtn:SetText("");
    end
    
    idx = QA.BFID2Index[bfName][bfID .. ""];
    
    if idx ~= 0 and idx > QA.LastBFsSeen[bfName].NumBFs - QA.JOIN_BUTTON_MAX then
        statusBtn:ClearAllPoints();
        statusBtn:SetPoint("LEFT", QA.JOIN_BUTTON_NAME .. (QA.LastBFsSeen[bfName].NumBFs - idx + 1), "RIGHT", 2, 0);
    else
        if idx == 0 then
            labelBtn:SetText(FIRST_AVAILABLE);
        else
            labelBtn:SetText( bfName .. " " .. bfID );
        end
        labelBtn:Show();
        statusBtn:ClearAllPoints();
        statusBtn:SetPoint("LEFT", labelBtn, "RIGHT", 2, 0);
    end
    
    statusBtn:Enable();
    statusBtn:Show();
    
end

function QA_UpdateStatusButtonTooltip( btn )
	local waitTime = GetBattlefieldEstimatedWaitTime(btn.QNum);
	local timeInQueue = GetBattlefieldTimeWaited(btn.QNum)/1000;
	
	if QA.QueueList[btn.QNum] and QA.QueueList[btn.QNum].Status == "queued" then
		if ( waitTime == 0 ) then
			waitTime = QUEUE_TIME_UNAVAILABLE;
		elseif ( waitTime < 60000 ) then 
			waitTime = LESS_THAN_ONE_MINUTE;
		else
			waitTime = SecondsToTime(waitTime/1000, 1);
		end
		btn.tooltip = string.format(QA.TEXT.TOOLTIP_IN_QUEUE, QA.QueueList[btn.QNum].BFName .. " " .. QA.QueueList[btn.QNum].ID, waitTime, SecondsToTime(timeInQueue));
	elseif QA.QueueList[btn.QNum] and QA.QueueList[btn.QNum].Status == "confirm" then
		btn.tooltip = string.format(QA.TEXT.TOOLTIP_QUEUE_CONFIRM, QA.QueueList[btn.QNum].BFName .. " " .. QA.QueueList[btn.QNum].ID, SecondsToTime(GetBattlefieldPortExpiration(btn.QNum)/1000));
	end
end

function QA_JoinButton_OnClick( btnText )
    --QADebugInfo("Join Button " .. btnText .. " Clicked.");
	
	local bfName;
	local i, idx;
	
	bfID = tonumber( string.match(btnText, "%d+") );
    bfName = QueueAssistantFrame.title;
    idx = QA.BFID2Index[bfName][bfID .. ""];
	
	for i=1, QA.MAX_BATTLEFIELD_QUEUES, 1 do
		if QA.QueueList[i] ~= nil and QA.QueueList[i].BFName == bfName and QA.QueueList[i].Status == "confirm" then
			if QA.QueueList[i].ID == bfID then
				return;
			end
			QA.PendingToJoin[bfName] = nil;
			QA.PendingToJoin[bfName] = {};
			QA.PendingToJoin[bfName].ID = bfID;
			QA.PendingToJoin[bfName].AddedTime = time();
			AcceptBattlefieldPort(i, nil);
		end
	end
	
	JoinBattlefield(idx);
end

function QA_JoinButtonStatus_OnClick( self, button )

	local queueInfo = QA.QueueList[self.QNum];
	if button == "RightButton" then
		AcceptBattlefieldPort( self.QNum, nil );
	elseif button == "LeftButton" then
		if queueInfo.Status == "confirm" then
			AcceptBattlefieldPort( self.QNum, 1 );
		end
	end	
end

function QA_JoinButton_OnEnter( btnID )
end

function QA_JoinButton_OnLeave( btnID )
end

function QA_LeaveAll_OnClick()
end

function QA_GetNewBattleFieldsStr( bfName )
    local sorted = {};
    local bfList;
    local i, prev;
    local str = "New battlefields of " .. bfName .. " could be: ";
    
    bfList = QA.LastBFsSeen[bfName];
    if bfList == nil or bfList.UpdatedTime == nil or bfList[0] == nil then
        return nil
    end
    
    for i=0, table.getn(bfList), 1 do
        table.insert( sorted, bfList[i]);
    end
    table.sort( sorted );
    --// Start debug info
    --QADebugInfo( " Last battle fields seen:" );
    --QADebugInfo( table.concat( sorted, ", "));
    --// End debug info

    prev = 0;
    for _, i in ipairs( sorted ) do
        if i-prev > 2 then
            str = str .. (prev+1) .. "-" .. (i-1) .. ", ";
        elseif i-prev > 1 then
            str = str .. (prev+1) .. ", ";
        end
        prev = i;
    end
    str = str .. ( sorted[table.getn(sorted)] + 1) .. " and greater.";
    --// Start debug info
    --QADebugInfo( str );
    --// End debug info
    return str;
end

